package jmine.tec.persist.schema.impl.elements;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import jmine.tec.persist.schema.api.AbstractProcessingSchemaElement;
import jmine.tec.persist.schema.api.SqlDialect;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
 * Elemento que representa uma check constraint
 * 
 * @author lundberg
 */
public class CheckConstraintElement extends AbstractProcessingSchemaElement<TableElement> implements Comparable<CheckConstraintElement> {

    private String table;

    private String name;

    private String condition;

    /**
     * Construtor
     * 
     * @param table tabela que possui a contraint
     * @param name nome da constraint
     * @param condition condição verificada
     * @param parent mensagens
     */
    public CheckConstraintElement(String table, String name, String condition, ColumnElement parent) {
        super(parent);
        this.table = table;
        this.name = name;
        this.condition = condition;
    }

    /**
     * {@inheritDoc}
     */
    public List<String> createStatements(SqlDialect dialect) {
        return dialect.renderCheckConstraint(this.table, this.name, this.getParent().getType().getCode(), this.condition);
    }

    /**
     * {@inheritDoc}
     */
    public int compareTo(CheckConstraintElement o) {
        CompareToBuilder builder = new CompareToBuilder();
        builder.append(this.table, o.table);
        builder.append(this.name, o.name);
        builder.append(this.condition, o.condition);
        return builder.toComparison();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof CheckConstraintElement)) {
            return false;
        }
        CheckConstraintElement o = (CheckConstraintElement) obj;
        return new EqualsBuilder().append(this.table, o.getTable()).append(this.condition, o.condition).isEquals();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(this.table).append(this.condition).toHashCode();
    }

    /**
     * @return nome da constraint
     */
    public String getName() {
        return this.name;
    }

    /**
     * @return nome da tabela que possui a restrição
     */
    public String getTable() {
        return this.table;
    }

    /**
     * @return condition
     */
    public String getCondition() {
        return this.condition;
    }

    /**
     * @param tableElement tabela
     */
    public void checkDuplicate(TableElement tableElement) {
        Set<String> existing = new HashSet<String>();
        boolean diferente = true;
        for (CheckConstraintElement fk : tableElement.getCheckConstraints()) {
            if (StringUtils.isNotBlank(fk.name)) {
                diferente = existing.add(fk.name);
                if (!diferente) {
                    this.warn(String.format("A Constraint '%s' está repetindo na tabela '%s',"
                            + " por favor altere o name da Check mudando o sufixo para um número maior.", fk.name, tableElement.getName()));
                }
            }
        }
    }

    /**
     * Obtém o nome da tabela e um nome próprio para a constraint é gerado, caso não tenha sido definido um.
     * 
     * @param tableElement tabela que possui a restrição
     */
    public void process(TableElement tableElement) {
        this.checkDuplicate(tableElement);
        this.table = tableElement.getName();

        Set<String> existing = new HashSet<String>();
        if (this.name == null || this.name.length() == 0) {
            String alias = tableElement.getAlias();
            for (CheckConstraintElement fk : tableElement.getCheckConstraints()) {
                existing.add(fk.name);
            }
            int i = 0;
            while (existing.contains(String.format("CK_%s_%s", alias, i))) {
                i++;
            }
            this.name = String.format("CK_%s_%s", alias, i);
            this.warn(String
                    .format("Check constraint %s definido automaticamente, por favor, passe o sufixo da constraint ao cria-lo na tabela %s e condição %s.",
                            this.name, this.table, this.condition));
        } else {
            if (!this.name.startsWith(String.format("CK_%s_", tableElement.getAlias()))) {
                this.warn(String.format(
                        "Nome de check constraint definido fora do padrão '%s' na tabela '%s', o correto é: CK_%s_ + Sufixo", this.name,
                        this.table, tableElement.getAlias()));
            }
        }

    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected ColumnElement getParent() {
        return (ColumnElement) super.getParent();
    }
}
